using System;
using System.Collections.Generic;
using System.Linq;
using FishFillets.Model;
using FishFillets.Model.GameItems;
using Microsoft.Xna.Framework;

namespace FishFillets.ObjectLogic
{
    /// <summary>
    /// 
    /// </summary>
    public class MoveRulesManager
    {
        public static bool CanMove(BaseItem item, Direction dir, ItemWeight maxWeight, List<BaseItem> items)
        {
            // najdi vsechny kolizni predmety, ktere bezprostredne vadi pohybu obj smerem dir
            List<BaseItem> collisons = GetCollisionObjects(item, dir, items);

            if (collisons.Any(o => o.Weight > maxWeight))
            {
                return false;
            }

            // na kazdy objekt zavolej canmove a jako parametr
            return collisons.Aggregate(true, (b, o) => b && CanMove(o, dir, maxWeight, items.Except(collisons).ToList()));
        }

        public static bool CanFall(BaseItem o, List<BaseItem> objects)
        {
            return CanMove(o, Direction.Down, ItemWeight.Heavy, objects);
        }

        private static List<BaseItem> GetCollisionObjects(BaseItem item, Direction dir, List<BaseItem> items)
        {
            List<Point> origColPoints = GetCollisionPoints(item, dir);
            List<BaseItem> collisons = new List<BaseItem>();
            foreach (BaseItem o in items)
            {
                List<Point> colPoints = GetCollisionPoints(o, null);
                if (colPoints.Intersect(origColPoints).Any())
                {
                    collisons.Add(o);
                }
            }
            return collisons;
        }

        public static List<BaseItem> GetMovedoObjects(BaseItem item, Direction dir, List<BaseItem> objects)
        {
            List<BaseItem> list = new List<BaseItem> { item };
            List<BaseItem> collisons = GetCollisionObjects(item, dir, objects.Except(list).ToList());
            List<BaseItem> nlist = objects.Except(list).Except(collisons).ToList();
            foreach (BaseItem collison in collisons)
            {
                list.AddRange(GetMovedoObjects(collison, dir, nlist).Except(list));
            }
            return list;
        }

        private static List<Point> GetCollisionPoints(BaseItem item, Direction? dir)
        {
            if (dir == null)
            {
                return GetObjetcPoints(item, 0, 0);
            }
            switch (dir.Value)
            {
                case Direction.Up:
                    return GetObjetcPoints(item, 0, -1);
                case Direction.Left:
                    return GetObjetcPoints(item, -1, 0);
                case Direction.Down:
                    return GetObjetcPoints(item, 0, 1);
                case Direction.Right:
                    return GetObjetcPoints(item, 1, 0);
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }

        private static List<Point> GetObjetcPoints(BaseItem item, int xd, int yd)
        {
            List<Point> list = new List<Point>();
            for (int y = 0; y < item.ItemMap.GetLength(1); y++)
            {
                for (int x = 0; x < item.ItemMap.GetLength(0); x++)
                {
                    if (item.ItemMap[x, y])
                    {
                        list.Add(new Point(item.Position.X + x + xd, item.Position.Y + y + yd));
                    }
                }
            }
            return list;
        }
    }
}
